//
//  MessageDidSyncEventProcessor.swift
//  app_rn_frontend
//
//  Created by 鱼涛 on 2017/12/15.
//  Copyright © 2017年 Facebook. All rights reserved.
//

import Foundation
import WCDBSwift
import AudioToolbox
import SwiftyJSON

class MessageDidSyncEventProcessor: EventProcessorProtocol {
    static let instance = MessageDidSyncEventProcessor()
    
    private init() {
    }
    
    let service = GroupService()
    
    private let cipher = Cipher(key: sec_key, iv: sec_iv)
    
    private let queue = DispatchQueue(label: "com.tst.db",qos:.background)
    
    func process(params: Dictionary<String, Any>?) throws {
        do {
            guard AppStatusManager.instance.userLoginStatus.0 && AppStatusManager.instance.userLoginStatus.1 != nil && AppStatusManager.instance.userLoginStatus.1 != 0 else {
                throw ProcessError.LoginStatusError
            }
            
            guard let pathForUserDB = AppStatusManager.instance.urlForUserDB?.path else {
                throw ProcessError.InvalidParamError
            }
            
            guard let messages = params?["msgs"] as? [Dictionary<String, Any>] else {
                throw ProcessError.InvalidParamError
            }
            
            let df = DateFormatter()
            df.dateFormat = "yyyy-MM-dd HH:mm:ss"
            let nowString = "\(df.string(from: Date()))"
            
            var needSyncSessionIdSet = Set<Int>()
            var messageArray = [Dictionary<String, Any>]()
            var needNotifyMsgArray = [Dictionary<String, Any>]()
            var maxSyncKey: Int = 0
            //            let db = try Connection(pathForUserDB)
            let db = Database(at: pathForUserDB)
            var disturb = false
            try db.run(transaction: { [unowned self]_ in
                for message in messages {
                    guard let syncKey = message["syncKey"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let localId = message["localId"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let chatType = message["cType"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let from = message["from"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let fromName = message["fromName"] as? String else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let to = message["to"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let messageContent = message["msg"] as? String else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let messageType = message["mType"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard var timestamp = message["ts"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    guard timestamp != 0 else {
                        throw ProcessError.InvalidParamError
                    }
                    guard let at = message["at"] as? Int else {
                        throw ProcessError.InvalidParamError
                    }
                    
                    guard let messageContent = cipher.decrypt(messageContent) else {
                        throw ProcessError.InvalidParamError
                    }
                    
                    timestamp /= 1000
                    let me: Int = from == AppStatusManager.instance.userLoginStatus.1 ? 1 : 0;
                    
                    var sessionServerId: Int = 0
                    var session = ""
                    if chatType == 1 {
                        sessionServerId = me == 1 ? to : from
                        session = "好友"
                    } else {
                        sessionServerId = to
                        session = "群聊"
                    }
                    let count = try db.getValue(on: Session.CodingKeys.any.count(), fromTable: Session.table, where: Session.CodingKeys.sessionServerId == sessionServerId && Session.CodingKeys.chatType == chatType)
                    if count.int64Value == 0 {
                        var sessionName = me == 1 ? "好友" : fromName
                        if (chatType == 1) {
                            if let friend: Contact = try db.getObject(fromTable: Contact.table, where: Contact.CodingKeys.userId == sessionServerId), let remarkName = friend.remarkName {
                                sessionName = remarkName
                            }
                        } else {
                            if let group: Group = try db.getObject(fromTable: Group.table, where: Group.CodingKeys.groupId == sessionServerId) {
                                if !group.noDisturb {
                                    disturb = true
                                }
                                sessionName = group.name ?? ""
                            } else {
                                try service.getGroupInfoFromRemote(sessionServerId,queue: queue)
                            }
                        }
                        let session = Session()
                        session.sessionServerId = sessionServerId
                        session.chatType = ChatType(rawValue: chatType) ?? ChatType.p2p
                        //                        session.name = sessionName
                        session.createdDatetime = nowString
                        session.updatedDatetime = nowString
                        try db.insert(session, intoTable: Session.table)
                        needSyncSessionIdSet.insert(session.lastInsertedRowID)
                    }
                    
                    guard sessionServerId != 0 else {
                        continue
                    }
                    
                    if let group: Group = try db.getObject(fromTable: Group.table, where: Group.CodingKeys.groupId == sessionServerId) {
                        if !group.noDisturb {
                            disturb = true
                        }
                    } else {
                        disturb = true
                    }
                    
                    
                    var sessionId: Int = 0
                    if let session: Session = try db.getObject(fromTable: Session.table, where: Session.CodingKeys.sessionServerId == sessionServerId && Session.CodingKeys.chatType == chatType) {
                        sessionId = session.id
                    }
                    
                    guard sessionId != 0 else {
                        continue
                    }
                    let sessionUpd = Session()
                    sessionUpd.updatedDatetime = nowString
                    sessionUpd.deleted = 0
                    try db.update(table: Session.table, on:[ Session.CodingKeys.updatedDatetime,Session.CodingKeys.deleted], with: sessionUpd, where: Session.CodingKeys.sessionServerId == sessionServerId && Session.CodingKeys.chatType == chatType)
                    
                    if let messageInDb: Message = try db.getObject(fromTable: Message.table, where: Message.CodingKeys.syncKey == syncKey) {
                        continue
                    }
                    
                    var messageId: Int = 0
                    let createdDatetime = "\(df.string(from: Date(timeIntervalSince1970: TimeInterval(timestamp))))"
                    
                    let msg = Message()
                    msg.sessionId = sessionId
                    msg.sessionServerId = sessionServerId
                    msg.chatType = ChatType(rawValue: chatType) ?? ChatType.p2p
                    msg.msgType = messageType
                    msg.syncKey = syncKey
                    msg.userId = from
                    msg.content = messageContent
                    msg.createTime = Date(timeIntervalSince1970: TimeInterval(timestamp))
                    msg.ts = Date(timeIntervalSince1970: TimeInterval(timestamp))
                    msg.me = me == 1
                    
                    if msg.me {
                        msg.audioRead = true
                        if let msgInDb: Message = try db.getObject(on: Message.CodingKeys.all, fromTable: Message.table, where: Message.CodingKeys.id == localId) {
                            msgInDb.syncKey = msg.syncKey
                            msgInDb.ts = msg.ts
                            try db.update(table: Message.table, on: [Message.CodingKeys.syncKey, Message.CodingKeys.audioRead,Message.CodingKeys.ts], with: msgInDb,where: Message.CodingKeys.id == localId)
                            //                            try db.insertOrReplace(msgInDb, intoTable: Message.table)
                            messageId = localId
                        } else {
                            //                            msg.id = localId
                            msg.unread = false
                            try db.insert(msg, intoTable: Message.table)
                            messageId = Int(msg.lastInsertedRowID) //拿到新插入的消息数据的localId
                        }
                    } else {
                        msg.audioRead = false
                        try db.insert(msg, intoTable: Message.table)
                        messageId = Int(msg.lastInsertedRowID) //拿到新插入的消息数据的localId
                        
                        //更新session的最新时间
                        let session = Session()
                        session.updatedDatetime = createdDatetime
                        try db.update(table: Session.table, on: Session.CodingKeys.updatedDatetime, with: session, where: Session.CodingKeys.id == sessionId)
                    }
                    
                    MsgHandlerFactory.instance.getHandler(msg).handler(msg)
                    
                    var message = Dictionary<String, Any>()
                    message["id"] = messageId
                    message["key"] = messageId
                    message["syncKey"] = syncKey
                    message["sessionId"] = sessionId
                    message["sessionServerId"] = sessionServerId
                    message["chatType"] = chatType
                    message["userId"] = from
                    message["messageType"] = messageType
                    message["messageContent"] = messageContent
                    message["fromName"] = fromName
                    message["me"] = me
                    message["userName"] = fromName
                    message["ts"] = TimeInterval(timestamp);
                    message["audioRead"] = msg.audioRead
                    if let from = try ContactDao.getContact(from) {
                        message["avatar"] = from.avatar
                    }
                    
                    messageArray.append(message)
                    if me != 1 || msg.messageType==MessageType.notice {
                        needNotifyMsgArray.append(message)
                    }
                }
                let message = try db.getValue(on: Message.CodingKeys.syncKey.asColumn().max(), fromTable: Message.table)
                maxSyncKey = Int(message.int64Value)
                
            })
            
            for sessionId in needSyncSessionIdSet {
                var sessionServerId: Int = 0
                do {
                    if let session: Session = try db.getObject(on: Session.CodingKeys.all, fromTable: Session.table, where: Session.CodingKeys.id == sessionId) {
                        sessionServerId = session.sessionServerId
                    }
                } catch let error as NSError {
                    print(error.localizedDescription)
                }
                
                guard sessionServerId != 0 else {
                    continue
                }
                
                IM.request(.getGroupUsers(groupId: sessionServerId), success: { (json) in
                    if let data = json["data"].array {
                        if data.count == 0 {
                            return
                        }
                        do {
                            try db.run(transaction: { _ in
                                for memberInfo in data {
                                    let userId = memberInfo["userId"].intValue
                                    let me = userId == AppStatusManager.instance.userLoginStatus.1
                                    let nickName = memberInfo["nickName"].string
                                    let member = GroupMember()
                                    member.groupId = sessionServerId
                                    member.userId = userId
                                    member.nickName = nickName
                                    member.me = me
                                    let count = try db.getValue(on: GroupMember.CodingKeys.any.count(), fromTable: GroupMember.table,
                                                                where: GroupMember.CodingKeys.groupId == sessionServerId && GroupMember.CodingKeys.userId == userId)
                                    if count.int32Value == 0 {
                                        try db.insert(member, intoTable: GroupMember.table)
                                    } else {
                                        try db.update(table: GroupMember.table, on: [GroupMember.CodingKeys.nickName], with: member,
                                                      where: GroupMember.CodingKeys.groupId == sessionServerId && GroupMember.CodingKeys.userId == userId)
                                    }
                                }
                            })
                            
                        }catch {
                            print(error)
                        }
                    }
                }, error: { (code) in
                    print(code)
                }) { (error) in
                    print(error)
                }
            }
            
            if (messageArray.count > 0) {
                
                var data = Dictionary<String, Any>()
                data["messageList"] = messageArray
                //发送新消息到达通知
                if needNotifyMsgArray.count > 0 {
                    let notificationNam = Notification.Name("onNewMsg")
                    NotificationCenter.default.post(name: notificationNam, object: nil, userInfo: ["list": needNotifyMsgArray])
                    if disturb {
                        DispatchQueue.main.async() {
                            //振动
                            let soundID = SystemSoundID(kSystemSoundID_Vibrate)
                            AudioServicesPlaySystemSound(soundID)
                        }
                    }
                }
                
                ConnectionManager.instance.syncMessage(userId: AppStatusManager.instance.userLoginStatus.1!, syncKey: maxSyncKey)
            } else {
                //消息全部同步完成,发送消息同步确认回执
                ConnectionManager.instance.syncMsgConfirm(userId: AppStatusManager.instance.userLoginStatus.1!, syncKey: maxSyncKey)
            }
        } catch {
            throw error
        }
    }
}
